﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CPPEI.EventBus
{
    public class EventBus
    {
        private static EventBus s_instance;
        private static object s_lockObj = new object();

        private static Dictionary<string, EventBus> s_eventBus = new Dictionary<string, EventBus>();

        /// <summary>
        /// name:default
        /// </summary>
        public static EventBus Instance
        {
            get
            {
                if (s_instance == null)
                {
                    lock (s_lockObj)
                    {
                        if (s_instance == null)
                        {
                            s_instance = new EventBus("default");
                            s_eventBus.Add("default", s_instance);
                        }
                    }
                }
                return s_instance;
            }
        }

        public static EventBus Create(string name)
        {
            if (s_eventBus.ContainsKey(name))
            {
                return s_eventBus[name];
            }
            else
            {
                EventBus eventBus = new EventBus(name);
                s_eventBus.Add(name, eventBus);
                return eventBus;
            }
        }

        private List<EventTypeMapping> _mappings = new List<EventTypeMapping>();

        public string Name { get; private set; }

        public EventBus(string name)
        {
            Name = name;
        }

        /// <summary>
        /// 注册事件接收者
        /// 新增：避免反复注册对象
        /// </summary>
        /// <param name="receiver"></param>
        public void Register(object receiver)
        {
            #region 检查是否被注册过
            for (int index = 0; index < _mappings.Count; index++)
            {
                EventTypeMapping mapping = _mappings[index];
                for (int entryIndex = 0; entryIndex < mapping.EntryList.Count; entryIndex++)
                {
                    if (mapping.EntryList[entryIndex].Receiver == receiver)
                    {
                        return;
                    }
                }
            }
            #endregion

            Type receiverType = receiver.GetType();
            MethodInfo[] methodInfoArr = receiverType.GetMethods();
            for (int index = 0; index < methodInfoArr.Length; index++)
            {
                MethodInfo methodInfo = methodInfoArr[index];
                SubscribeAttribute attribute = Util.GetAttribute <SubscribeAttribute>(methodInfo);
                if (attribute != null)
                {
                    attribute.EventType = methodInfo.GetParameters()[0].ParameterType;

                    EventTypeMapping existMapping = null;
                    for (int mappingIndex = 0; mappingIndex < _mappings.Count; mappingIndex++)
                    {
                        EventTypeMapping mapping = _mappings[mappingIndex];
                        if (mapping.EventType == attribute.EventType)
                        {
                            existMapping = mapping;
                            break;
                        }
                    }
                    if (existMapping != null)
                    {
                        List<EventEntry> list = existMapping.EntryList;
                        list.Add(new EventEntry(receiver, receiverType, methodInfo));
                    }
                    else
                    {
                        List<EventEntry> list = new List<EventEntry>();
                        list.Add(new EventEntry(receiver, receiverType, methodInfo));
                        EventTypeMapping newMapping = new EventTypeMapping(attribute.EventType, list);
                        _mappings.Add(newMapping);
                    }
                }
            }
        }

        [Obsolete("暂不支持")]
        public void Register(Type receiverType)
        {
        }

        /// <summary>
        /// 取消事件接受者注册
        /// </summary>
        /// <param name="receiver"></param>
        public void Unregister(object receiver)
        {
            Type receiverType = receiver.GetType();
            MethodInfo[] methodInfoArr = receiverType.GetMethods();
            for (int index = 0; index < methodInfoArr.Length; index++)
            {
                MethodInfo methodInfo = methodInfoArr[index];
                SubscribeAttribute attribute = Util.GetAttribute<SubscribeAttribute>(methodInfo);
                if (attribute != null)
                {
                    attribute.EventType = methodInfo.GetParameters()[0].ParameterType;

                    for (int mappingIndex = 0; mappingIndex < _mappings.Count; mappingIndex++)
                    {
                        EventTypeMapping mapping = _mappings[mappingIndex];
                        if (mapping.EventType == attribute.EventType)
                        {
                            List<EventEntry> list = mapping.EntryList;
                            for (int entryIndex = 0; entryIndex < list.Count; )
                            {
                                EventEntry entry = list[entryIndex];
                                if (entry.Receiver == receiver)
                                {
                                    list.RemoveAt(entryIndex);
                                }
                                else
                                {
                                    entryIndex++;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 发布事件
        /// </summary>
        /// <param name="event"></param>
        public void Publish(object @event)
        {
            Type eventType = @event.GetType();
            for (int mappingIndex = 0; mappingIndex < _mappings.Count; mappingIndex++)
            {
                EventTypeMapping mapping = _mappings[mappingIndex];
                if (mapping.EventType == eventType)
                {
                    List<EventEntry> list = mapping.EntryList;
                    for (int index = 0; index < list.Count; index++)
                    {
                        EventEntry entry = list[index];
                        
                        //Console.WriteLine(string.Format("EventBus-转发事件:{0},接受者:{1},接收方法:{2},thread:{3}",
                        //    eventType, entry.ReceiverType, entry.CallMethod,
                        //    System.Threading.Thread.CurrentThread.ManagedThreadId));

                        try
                        {
                            entry.CallMethod.Invoke(entry.Receiver, new object[] { @event });
                        }
                        catch (Exception ex)
                        {
                            //Coolzon.Common.Logger.ErrorToTag("EventBus", "发布事件{0}到{1},出错:{2}",
                            //    eventType,
                            //    entry.ReceiverType,
                            //    ex);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 发布事件，异步执行
        /// </summary>
        /// <param name="event"></param>
        public void PublishAsync(object @event)
        {
            Type eventType = @event.GetType();
            for (int mappingIndex = 0; mappingIndex < _mappings.Count; mappingIndex++)
            {
                EventTypeMapping mapping = _mappings[mappingIndex];
                if (mapping.EventType == eventType)
                {
                    List<EventEntry> list = mapping.EntryList;
                    for (int index = 0; index < list.Count; index++)
                    {
                        EventEntry entry = list[index];

                        //Common.Logger.DebugToTag("EventBus", "EventBus:{0}({1})-转发事件:{2},接受者:{3},接收方法:{4}",
                        //    this.Name,
                        //    this.GetHashCode(),
                        //    eventType,
                        //    entry.ReceiverType,
                        //    entry.CallMethod);

                        InvokeParam invokeParam = new InvokeParam();
                        invokeParam.Entry = entry;
                        invokeParam.Param = @event;

                        AsyncExecute asyncExecute = new AsyncExecute(OnAsyncMethod);
                        asyncExecute.BeginInvoke(invokeParam, OnAsyncExecuteCallback, asyncExecute);
                    }
                }
            }
        }

        //异步调用的方法
        static void OnAsyncMethod(InvokeParam invokeParam)
        {
            //Console.WriteLine("EventBus-异步方法正执行");
            try
            {
                invokeParam.Entry.CallMethod.Invoke(invokeParam.Entry.Receiver, new object[] { invokeParam.Param });
            }
            catch (Exception ex)
            {
                //Coolzon.Common.Logger.ErrorToTag("EventBus", "异步发布事件{0}到{1},出错:{2}",
                //    invokeParam.Param.GetType(),
                //    invokeParam.Entry.ReceiverType,
                //    ex);
            }
        }

        //回调方法
        static void OnAsyncExecuteCallback(IAsyncResult result)
        {
            AsyncExecute asyncExecute = (AsyncExecute)result.AsyncState;
            asyncExecute.EndInvoke(result);
            //Console.WriteLine("EventBus-异步方法执行完毕");
        }
    }
}
